package edu.gatech.ors.mrsim.io.conf;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.IllegalFormatException;
import java.util.InvalidPropertiesFormatException;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import org.apache.log4j.Logger;

import edu.gatech.ors.mrsim.exceptions.WrongClassTypeException;
import edu.gatech.ors.mrsim.helpers.StringUtil;
import edu.gatech.ors.mrsim.io.logging.Logging;

/**
 * This is a simple representation of a generic configuration of a module or the entire simulator. As in Hadoop, it stores
 * key-value pairs with the keys being strings and the values being ints, boolean, strings, or other types. It extends Java's
 * Properties class and so can store in XML as well as plaintext files. A default configuration file is specified, though
 * this supports other files as well. This class can also be used for temporary runtime configuration that is never saved to
 * disk.
 * 
 * @author Andrey Kurenkov (akurenkov3@gatech.edu)
 * @version 1.1
 */
public class PropertiesConfiguration extends Properties implements Configuration {
	private File propertyFile;
	private boolean isXML;
	private static final String PATH_TO_CORE = "conf" + File.separator + "default_conf.xml";
	private static final String PATH_TO_TEST = "conf" + File.separator + "test_conf.xml";
	private static final String PATH_TO_DEBUG = "conf" + File.separator + "debug_conf.xml";
	private static Configuration globalConfig;
	private static final File CORE_FILE = new File(PATH_TO_CORE);
	private static final File TEST_FILE = new File(PATH_TO_TEST);
	private static final File DEBUG_FILE = new File(PATH_TO_DEBUG);

	private final static BasicLoader BASIC_CLASS_LOADER = new BasicLoader();
	private boolean checkFinal, checkIgnoreCase, checksDisabled;

	private boolean reservedException;

	private final static Logger logger = Logging.getInstance().getClassLogger(PropertiesConfiguration.class);

	/**
	 * Plain constructor that creates a configuration not attached to any file on the filesystem and containing no values.
	 */
	public PropertiesConfiguration() {
		super();
	}

	/**
	 * Simple constructor that creates an empty set of configuration not associated with any file. If any keys are retrieved
	 * that have not been entered into this config, it will search defaultVals and return either a value inside that or null.
	 * 
	 * @param defaultVals
	 *            the default values to use for retrieval
	 */
	public PropertiesConfiguration(PropertiesConfiguration defaultVals) {
		super(defaultVals);
		checkFinal = this.containsKey(FINAL);
		checkIgnoreCase = this.contains(IGNORE_CASE);
	}

	/**
	 * Overriden version of setProperty from properties. Keys that equal values in the RESERVED array are not overwritten. If
	 * the properties file has a FINAL key its value is retrieved as a set of String through getStrings, and if any of those
	 * strings equals the provided key it is not overwritten. Otherwise super.setProperty is invoked and returned.
	 * 
	 * @param key
	 *            the key of the key-value pair
	 * @param value
	 *            the value of the key-value pair
	 * @return null if key is null or key in RESERVED or key in value of FINAL, otherwise result from super.setProperty
	 */
	@Override
	public Object setProperty(String key, String value) {
		if (!checksDisabled) {
			if (key == null)
				return null;
			if (Arrays.asList(RESERVED).contains(key)) {
				if (reservedException) {
					reservedException = false;
				} else
					return null;
			}
			if (checkFinal) {
				List<String> finalStrings = this.getFinalKeys();
				if (finalStrings != null && finalStrings.contains(key)) {
					return null;
				} else {
					logger.error("FINAL keys in Configuration set to null");
					checkFinal = false;
				}
			}
			if (checkIgnoreCase) {
				Boolean ignore = this.isIgnoreCase();
				if (ignore != null) {
					if (ignore)
						return super.setProperty(key.toLowerCase(), value);
				} else {
					checkFinal = false;
				}
			}
		}
		return super.setProperty(key, value);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#getProperty(java.lang.String)
	 */
	@Override
	public String getProperty(String key) {
		if (!checksDisabled && checkIgnoreCase) {
			Boolean ignore = this.isIgnoreCase();
			if (ignore != null) {
				if (ignore)
					return super.getProperty(key.toLowerCase());
			} else {
				checkFinal = false;
			}
		}
		return super.getProperty(key);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#setAdditionalLogicEnabled(boolean)
	 */
	@Override
	public void setAdditionalLogicEnabled(boolean setTo) {
		checksDisabled = setTo;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#getFinalKeys()
	 */
	@Override
	public List<String> getFinalKeys() {
		List<String> keys = null;
		if (this.containsKey(FINAL)) {
			keys = Arrays.asList(getStrings(FINAL));
		}
		return keys;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#setKeyFinal(java.lang.String)
	 */
	@Override
	public boolean setKeyFinal(String key) {
		List<String> keys = getFinalKeys();
		if (keys == null) {
			setProperty(FINAL, key);
		} else {
			if (!keys.contains(key)) {
				keys.add(key);
				reservedException = true;
				this.setObjectsAsStrings(FINAL, keys.toArray());
			} else
				return false;
		}
		return true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#setIgnoreCase(boolean)
	 */
	@Override
	public void setIgnoreCase(boolean setTo) {
		reservedException = true;
		setBoolean(IGNORE_CASE, setTo);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#isIgnoreCase()
	 */
	@Override
	public boolean isIgnoreCase() {
		if (checkIgnoreCase) {
			try {
				Boolean bool = this.getBoolean(IGNORE_CASE);
				if (bool == null) {
					checkIgnoreCase = false;
				}
				return bool.booleanValue();
			} catch (NumberFormatException e) {
				logger.error("Ignore case wrong format in configuration");
			}
		}
		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#setKeyMutable(java.lang.String)
	 */
	@Override
	public boolean setKeyMutable(String key) {
		List<String> keys = getFinalKeys();
		if (keys == null) {
			return false;
		} else {
			if (keys.contains(key)) {
				keys.remove(key);
				this.setObjectsAsStrings(FINAL, keys.toArray());
			} else
				return false;
		}
		return true;
	}

	/**
	 * More general constructor for existing configuration file. The code that supplies this file is expected to handle
	 * exceptions with it not existing or not actually being a configuration file (as in, saved from Configuration itself
	 * previously).
	 * 
	 * @param loadFrom
	 *            the file to load the Configuration from. Must be the result of calling the write method of Configuration
	 *            previously or already formatted correctly for Java's Properties class.
	 * @throws InvalidPropertiesFormatException
	 *             See the JDK Properties class
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	public PropertiesConfiguration(File loadFrom) throws InvalidPropertiesFormatException, FileNotFoundException,
			IOException {
		propertyFile = loadFrom;
		isXML = loadFrom.getName().endsWith(".xml");
		if (loadFrom.exists()) {
			if (isXML)
				loadFromXML(new FileInputStream(loadFrom));
			else
				load(new FileInputStream(loadFrom));

			checkFinal = this.containsKey(FINAL);
			checkIgnoreCase = this.contains(IGNORE_CASE);
		} else {
			if (isXML)
				storeToXML(new FileOutputStream(loadFrom), "Configuration created with file " + loadFrom.getName()
						+ " at time " + StringUtil.getFormattedTime());
			else
				store(new FileOutputStream(loadFrom), "Configuration created with file " + loadFrom.getName() + " at time "
						+ StringUtil.getFormattedTime());
		}
	}

	/**
	 * Static getter/intializer for a configuration based on a hardcoded default file. Since this file is specified and
	 * constant, it is expected to exist and programmers using it should generally not handle the case where there are
	 * exceptions(unlike the public constructor). Problems with this file are thus logged but not generally expected and so
	 * not thrown.
	 * 
	 * @param create
	 *            whether to create the file if it does not exist
	 */
	public static PropertiesConfiguration loadCore(boolean create) {
		return loadFromFile(CORE_FILE, create);
	}

	/**
	 * Static getter/intializer for a configuration based on a hardcoded test file. Since this file is specified and
	 * constant, it is expected to exist and programmers using it should generally not handle the case where there are
	 * exceptions(unlike the public constructor). Problems with this file are thus logged but not generally expected and so
	 * not thrown.
	 * 
	 * @param create
	 *            whether to create the file if it does not exist
	 */
	public static PropertiesConfiguration loadTest(boolean create) {
		return loadFromFile(TEST_FILE, create);
	}

	/**
	 * Static getter/intializer for a configuration based on a hardcoded debug file. Since this file is specified and
	 * constant, it is expected to exist and programmers using it should generally not handle the case where there are
	 * exceptions(unlike the public constructor). Problems with this file are thus logged but not generally expected and so
	 * not thrown.
	 * 
	 * @param create
	 *            whether to create the file if it does not exist
	 */
	public static PropertiesConfiguration loadDebug(boolean create) {
		return loadFromFile(DEBUG_FILE, create);
	}

	/**
	 * Helper method to load from Files while avoiding possible exceptions thrown by using constructor.
	 * 
	 * @param load
	 *            the file to load from
	 * @param create
	 *            whether to create the file if it does not exist
	 */
	public static PropertiesConfiguration loadFromFile(File load, boolean create) {
		if (!load.exists())
			if (!create)
				return null;
		try {
			PropertiesConfiguration config = null;
			if (!load.exists()) {
				boolean created = load.getParentFile().exists();
				if (!created) {
					created = load.getParentFile().mkdirs();
					if (!created)
						return null;
				}
				created = load.createNewFile();
				if (!created)
					return null;

				config = new PropertiesConfiguration();
				if (load.getName().endsWith(".xml")) {
					config.storeToXML(new FileOutputStream(load), "Created new config at path " + load.getAbsolutePath()
							+ " on " + StringUtil.getFormattedTime());
				}
			} else
				config = new PropertiesConfiguration(load);

			return config;
		} catch (InvalidPropertiesFormatException e) {
			logger.error(StringUtil.getStackTrace(e));
		} catch (IOException e) {
			logger.error(StringUtil.getStackTrace(e));
		}
		return null;// if exception
	}

	/**
	 * Sets a new static global configuration, as long as the configuration is not null
	 * 
	 * @param newGlobal
	 *            the new global Configuration
	 * @throws NullPointerException
	 *             is the given Configuration is null
	 */
	public static void setGlobalConfig(Configuration newGlobal) throws NullPointerException {
		if (newGlobal != null)
			globalConfig = newGlobal;
		else
			throw new NullPointerException("Trying to set a null global configuration is not allowed");
	}

	/**
	 * Retrieves the global configuration. If it has not been set yet, it loads the default configuration file and sets that
	 * to be global before returning it.
	 * 
	 * @return the static global configuration
	 */
	public static Configuration getGlobalConfig() {
		if (globalConfig == null)
			globalConfig = PropertiesConfiguration.loadCore(true);
		return globalConfig;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#getBoolean(java.lang.String)
	 */
	@Override
	public Boolean getBoolean(String key) {
		return (containsKey(key) ? Boolean.parseBoolean(getProperty(key)) : null);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#getBooleans(java.lang.String)
	 */
	@Override
	public boolean[] getBooleans(String key) {
		if (!containsKey(key))
			return null;
		else {
			try {
				String[] strings = getStrings(key);
				boolean[] booleans = new boolean[strings.length];
				for (int i = 0; i < booleans.length; i++) {
					try {
						booleans[i] = Boolean.parseBoolean(strings[i]);
					} catch (IllegalFormatException e) {
						return null;
					}
				}
				return booleans;
			} catch (Exception e) {
				logger.error(StringUtil.getStackTrace(e));
				return null;
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#getClass(java.lang.String)
	 */
	@Override
	public Class<?> getClass(String key) throws ClassNotFoundException {
		return getClass(key, BASIC_CLASS_LOADER);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#getClass(java.lang.String,
	 * edu.gatech.ors.mrsim.io.conf.ConfigurationClassLoader)
	 */
	@Override
	public Class<?> getClass(String key, ConfigurationClassLoader loader) throws ClassNotFoundException {
		return loader.loadClass(key, this);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#getClasses(java.lang.String)
	 */
	@Override
	public Class<?>[] getClasses(String key) throws ClassNotFoundException {
		return getClasses(key, BASIC_CLASS_LOADER);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#getClasses(java.lang.String,
	 * edu.gatech.ors.mrsim.io.conf.ConfigurationClassLoader)
	 */
	@Override
	public Class<?>[] getClasses(String key, ConfigurationClassLoader loader) throws ClassNotFoundException {
		return loader.loadClasses(key, this);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#getFile(java.lang.String)
	 */
	@Override
	public File getFile(String key) {
		if (!containsKey(key))
			return null;
		else
			return new File(getProperty(key));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#getDouble(java.lang.String)
	 */
	@Override
	public Double getDouble(String key) {
		if (!containsKey(key))
			return null;
		else {
			try {
				return Double.parseDouble(getProperty(key));
			} catch (Exception e) {
				logger.error(StringUtil.getStackTrace(e));
				return null;
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#getDoubles(java.lang.String)
	 */
	@Override
	public double[] getDoubles(String key) {
		if (!containsKey(key))
			return null;
		else {
			try {
				String[] strings = getStrings(key);
				double[] doubles = new double[strings.length];
				for (int i = 0; i < doubles.length; i++) {
					try {
						doubles[i] = Double.parseDouble(strings[i]);
					} catch (IllegalFormatException e) {
						return null;
					}
				}
				return doubles;
			} catch (Exception e) {
				logger.error(StringUtil.getStackTrace(e));
				return null;
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#getInt(java.lang.String)
	 */
	@Override
	public Integer getInt(String key) {
		if (!containsKey(key))
			return null;
		else {
			try {
				return Integer.parseInt(getProperty(key));
			} catch (Exception e) {
				logger.error(StringUtil.getStackTrace(e));
				return null;
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#getInts(java.lang.String)
	 */
	@Override
	public int[] getInts(String key) {
		if (!containsKey(key))
			return null;
		else {
			try {
				String[] strings = getStrings(key);
				int[] ints = new int[strings.length];
				for (int i = 0; i < ints.length; i++) {
					try {
						ints[i] = Integer.parseInt(strings[i]);
					} catch (IllegalFormatException e) {
						return null;
					}
				}
				return ints;
			} catch (Exception e) {
				logger.error(StringUtil.getStackTrace(e));
				return null;
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#getLong(java.lang.String)
	 */
	@Override
	public Long getLong(String key) {
		if (!containsKey(key))
			return null;
		else {
			try {
				return Long.parseLong(getProperty(key));
			} catch (Exception e) {
				logger.error(StringUtil.getStackTrace(e));
				return null;
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#getLongs(java.lang.String)
	 */
	@Override
	public long[] getLongs(String key) {
		if (!containsKey(key))
			return null;
		else {
			try {
				String[] strings = getStrings(key);
				long[] longs = new long[strings.length];
				for (int i = 0; i < longs.length; i++) {
					try {
						longs[i] = Long.parseLong(strings[i]);
					} catch (IllegalFormatException e) {
						return null;
					}
				}
				return longs;
			} catch (Exception e) {
				logger.error(StringUtil.getStackTrace(e));
				return null;
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#getStrings(java.lang.String)
	 */
	@Override
	public String[] getStrings(String key) {
		if (!containsKey(key))
			return null;
		return getProperty(key).split(LIST_SEPERATOR);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#reloadConfiguration()
	 */
	@Override
	public boolean reloadConfiguration() {
		if (propertyFile != null && this.propertyFile.exists()) {
			FileInputStream reader = null;
			try {
				reader = new FileInputStream(propertyFile);

				if (isXML)
					loadFromXML(reader);
				else
					load(reader);
				return true;
			} catch (InvalidPropertiesFormatException e) {

				logger.error(StringUtil.getStackTrace(e));
			} catch (FileNotFoundException e) {

				logger.error(StringUtil.getStackTrace(e));
			} catch (IOException e) {

				logger.error(StringUtil.getStackTrace(e));
			} finally {
				if (reader != null)
					try {
						reader.close();
					} catch (IOException e) {
						logger.error(StringUtil.getStackTrace(e));
					}
			}
		}
		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#saveConfiguration()
	 */
	@Override
	public boolean saveConfiguration() {
		return saveConfiguration(false);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#saveConfiguration(boolean)
	 */
	@Override
	public boolean saveConfiguration(boolean create) {
		if (propertyFile != null && this.propertyFile.exists()) {
			FileOutputStream writer = null;
			try {
				writer = new FileOutputStream(propertyFile);

				if (isXML)
					storeToXML(writer, "Last saved at time " + StringUtil.getFormattedTime());

				else
					store(writer, "Last saved at time " + StringUtil.getFormattedTime());
				return true;
			} catch (InvalidPropertiesFormatException e) {
				logger.error(StringUtil.getStackTrace(e));
			} catch (FileNotFoundException e) {
				logger.error(StringUtil.getStackTrace(e));
			} catch (IOException e) {
				logger.error(StringUtil.getStackTrace(e));
			} finally {
				if (writer != null)
					try {
						writer.close();
					} catch (IOException e) {
						logger.error(StringUtil.getStackTrace(e));
					}
			}
		}
		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#setBoolean(java.lang.String, boolean)
	 */
	@Override
	public String setBoolean(String key, boolean value) {
		if (key != null) {
			setProperty(key, Boolean.toString(value));
			return getProperty(key);
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#setBooleans(java.lang.String, java.lang.Boolean)
	 */
	@Override
	public String setBooleans(String key, Boolean... values) {
		if (key != null) {
			return setObjectsAsStrings(key, (Object[]) values);
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#setClass(java.lang.String, java.lang.Class, java.lang.Class)
	 */
	@Override
	public String setClass(String key, Class<?> theClass, Class<?> superType) throws WrongClassTypeException {
		if (key != null)
			if (superType.isAssignableFrom(theClass)) {
				setProperty(key, theClass.toString());
				return getProperty(key);
			} else {
				throw new WrongClassTypeException("Tried saving " + theClass + " as a class of type of " + superType + ".",
						superType);
			}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#setClass(java.lang.String, java.lang.Class)
	 */
	@Override
	public String setClass(String key, Class<?> theClass) {
		if (key != null) {
			setProperty(key, theClass.getName());
			return getProperty(key);
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#setClasses(java.lang.String, java.lang.Class)
	 */
	@Override
	public String setClasses(String key, Class<?>... classes) {
		String[] names = new String[classes.length];
		for (int i = 0; i < classes.length; i++)
			names[i] = classes[i].getName();
		return setStrings(key, names);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#setIfUnset(java.lang.String, java.lang.String)
	 */
	@Override
	public boolean setIfUnset(String key, String value) {
		if (key != null && !containsKey(key)) {
			setProperty(key, value);
			return true;
		}
		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#setInt(java.lang.String, int)
	 */
	@Override
	public String setInt(String key, int value) {
		if (key != null) {
			setProperty(key, Integer.toString(value));
			return getProperty(key);

		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#setInts(java.lang.String, java.lang.Integer)
	 */
	@Override
	public String setInts(String key, Integer... values) {
		if (key != null) {
			return setObjectsAsStrings(key, (Object[]) values);
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#setLong(java.lang.String, long)
	 */
	@Override
	public String setLong(String key, long value) {
		if (key != null) {
			setProperty(key, Long.toString(value));
			return getProperty(key);
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#setLongs(java.lang.String, java.lang.Long)
	 */
	@Override
	public String setLongs(String key, Long... values) {
		if (key != null) {
			return setObjectsAsStrings(key, (Object[]) values);
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#setDouble(java.lang.String, long)
	 */
	@Override
	public String setDouble(String key, long value) {
		if (key != null) {
			setProperty(key, Double.toString(value));
			return getProperty(key);
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#setDoubles(java.lang.String, java.lang.Double)
	 */
	@Override
	public String setDoubles(String key, Double... values) {
		if (key != null) {
			return setObjectsAsStrings(key, (Object[]) values);
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#setStrings(java.lang.String, java.lang.String, java.lang.String)
	 */
	@Override
	public String setStrings(String key, String seperator, String... values) {
		return setObjectsAsStrings(key, seperator, (Object[]) values);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#setStrings(java.lang.String, java.lang.String)
	 */
	@Override
	public String setStrings(String key, String... values) {
		return setObjectsAsStrings(key, LIST_SEPERATOR, (Object[]) values);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#setObjectsAsStrings(java.lang.String, java.lang.Object)
	 */
	@Override
	public String setObjectsAsStrings(String key, Object... values) {
		return setObjectsAsStrings(key, LIST_SEPERATOR, values);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#setObjectsAsStrings(java.lang.String, java.lang.String,
	 * java.lang.Object)
	 */
	@Override
	public String setObjectsAsStrings(String key, String seperator, Object... values) {
		if (key != null) {
			StringBuilder builder = new StringBuilder();
			for (Object obj : values) {
				builder.append(LIST_SEPERATOR + obj.toString());
			}
			builder.delete(0, LIST_SEPERATOR.length());
			setProperty(key, builder.toString());
			return getProperty(key);
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#setString(java.lang.String, java.lang.String,
	 */
	@Override
	public String setString(String key, String value) {
		return this.getProperty(key, value);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#getString(java.lang.String)
	 */
	@Override
	public String getString(String key) {
		return this.getProperty(key);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#setFile(java.io.File)
	 */
	@Override
	public void setFile(File setTo) {
		this.propertyFile = setTo;

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#containsKey()
	 */
	@Override
	public boolean containsKey(String key) {
		return super.containsKey(key);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#getValues()
	 */
	@Override
	public Collection<Object> getValues() {
		return super.values();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#getKeys()
	 */
	@Override
	public Set<String> getKeys() {
		return super.stringPropertyNames();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#getSubset()
	 */
	@Override
	public Configuration getSubset(String[] keys) {
		PropertiesConfiguration newConfig = new PropertiesConfiguration();
		for (String key : keys) {
			if (this.containsKey(key)) {
				newConfig.setProperty(key, this.getProperty(key));
			}
		}
		return newConfig;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.gatech.ors.mrsim.io.conf.Configuration#getFile()
	 */
	@Override
	public File getFile() {
		return propertyFile;
	}
}
